<#include "license.ftl">
<@license/>
package ${doc.all.package}.service;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.PrintWriter;
import java.util.logging.Logger;
import java.util.Arrays;
import java.util.List;
import redora.configuration.rdo.service.base.UpgradeBase;
import redora.exceptions.*;
import ${doc.all.package}.service.check.*;
import ${doc.all.package}.sql.*;

import static java.util.logging.Level.INFO;

/**
* Provides functions to create drop and check tables. The check function will see if the
* object model is in sync with the database.
* @author Redora (www.redora.net)
*/
public class Upgrade extends UpgradeBase {
    private static final transient Logger l = Logger.getLogger("${doc.all.package}.model.service.Upgrade");

    public static final List<String> objects = Arrays.asList(
<#list doc.all.object.@name?sort as modelname>
        "${modelname}"<#if modelname_has_next>,</#if>
</#list>
    );

    public static final String[] relationTables = {
<#list doc["/all/object/attributes/set[@multiplicity='n-to-m']"] as att>
        "${att.@relationTableName}"<#if att_has_next>,</#if>
</#list>
    };
    public static final String[] scripts = {
<#list doc["/all/upgrades/upgrade"] as upgrade>
        "${upgrade}"<#if upgrade_has_next>,</#if>
</#list>
    };
    
    public Upgrade() throws ConnectException {
        super("default");
    }

    @Override
    public void close() {
        super.close();
    }

    @Override
    public List<String> objects() {
        return objects;
    }
    @Override
    public String[] relationTables() {
        return relationTables;
    }
    @Override
    public String[] scripts() {
        return scripts;
    }

    /**
    * Creates table(s) that yet do not exist.
    * @param table (Optional) If null, all tables are checked
    * @param redora If true, also the Redora system tables are created
    */
    public void create(@Nullable String table, boolean redora) throws ConnectException, QueryException {
        execute("set foreign_key_checks = 0"); //tables can be created in any order
<#list doc.all.object.@name?sort as modelname>
        if ((table == null || table.equals("${modelname}")) && !tableExists("${modelname}")) {
            l.log(INFO, "Table ${modelname} does not exist, i will create it now.");
            execute(${modelname}SQL.CREATE_TABLE);
        }
    <#list doc["all/object/attributes/set[@multiplicity='n-to-m']"] as att>
        <#if att?parent?parent.@name == modelname>
        if ((table == null || table.equals("${modelname}")) && !tableExists("${att.@relationTableName}")) {
            l.log(INFO, "Table ${att.@relationTableName} does not exist, i will create it now.");
            execute(${modelname}SQL.CREATE_${att.@relationTableName?upper_case}_TABLE);
        }
        </#if>
    </#list>
</#list>
        execute("set foreign_key_checks = 1");

        if (redora) {
            redora.configuration.rdo.service.Upgrade up = new redora.configuration.rdo.service.Upgrade();
            up.create(null, false);
            up.close();
        }
    }

    /**
    * Drops given table or drops all, including the redora tables.
    * If a table is connected to a relation table (n-to-m relationship)
    * this table is also deleted.
    * @param table (Optional) If null, all tables are checked
    * @param recreate If true, the create table is invoke after the drop
    * @param redora If true, also the Redora system tables are created
    */
    public void drop(@Nullable String table, boolean recreate, boolean redora) throws RedoraException {
        execute("set foreign_key_checks = 0"); //tables can be created in any order
<#list doc.all.object.@name?sort as modelname>
        if ((table == null || table.equals("${modelname}")) && tableExists("${modelname}")) {
           l.log(INFO, "Table ${modelname} exists, i will drop it now.");
           execute("drop table `${modelname}`");
        }
    <#list doc["all/object/attributes/set[@multiplicity='n-to-m']"] as att>
        <#if att?parent?parent.@name == modelname>
        if ((table == null || table.equals("${modelname}")) && tableExists("${att.@relationTableName}")) {
            l.log(INFO, "Table ${att.@relationTableName} does not exist, i will create it now.");
            execute("drop table ${att.@relationTableName}");
        }
        </#if>
    </#list>
</#list>
        if (redora) {
            redora.configuration.rdo.service.Upgrade up = new redora.configuration.rdo.service.Upgrade();
            up.drop(null, false, false); //This is done just further on
            up.close();
        }

        if (recreate) {
            create(table, redora);
            initUpgradeScript();
        }
    }

    /**
    * Checks all tables and object models. Warns for models that are not in sync with thee
    * database. Also finds tables that do not have an object model.
    * @param out HTML stream with the warnings
    */
    public void check(@NotNull PrintWriter out) throws ConnectException, QueryException {
<#list doc.all.object.@name?sort as modelname>
        ${modelname?cap_first}TableCheck ${modelname?lower_case}Check = new ${modelname?cap_first}TableCheck(st);
        ${modelname?lower_case}Check.check(out);
</#list>
<#list doc["/all/object/attributes/set[@multiplicity='n-to-m']"] as att>
        checkRelationTable("${att.@relationTableName}","${att.@theirName}Id","${att.@myName}Id",out);
</#list>
    }

    /**
    * Drops and re-creates all tables. Runs the upgrade scripts.
    * @param args   Not used
    */
    public static void main(String[] args) throws Exception {
        Upgrade up = new Upgrade();
        up.drop(null, true, true);
        up.upgradeTables(new PrintWriter(System.out), null);
        up.close();
    }
}
